﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using UnityEngine;
using System.Net;

class PendingSendMessage
{
    public EMessageType m_Type;
    public BaseMessage m_Msg;
    public uint m_UserID;
}

public enum EMessageType
{
    NONE,
    HELLO,
    ACK,
    PING,
    JOIN,
    QUIT,
    SYNC,
}

public class BaseMessage
{
    public static UniqueIDGenerator m_SeqIDGenerator = new UniqueIDGenerator();
    public uint m_Seq;
    protected List<byte> m_TempList = new List<byte>();
    public BaseMessage()
    {
        m_Seq = m_SeqIDGenerator.GetUniqueID();
    }

    public virtual byte[] Serialize()
    {
        m_TempList.Clear();
        m_TempList.AddRange(BitConverter.GetBytes(m_Seq));
        return m_TempList.ToArray();
    }

    public virtual int Deserialize(byte[] bs, int offset = 0, int count = 0)
    {
        if (bs.Length - offset < 4) { return offset; }
        m_Seq = BitConverter.ToUInt32(bs, offset);
        return offset + 4;
    }
}

public class HelloMessage : BaseMessage
{
    //public string m_Host;
    public uint m_UserID;

    public override byte[] Serialize()
    {
        base.Serialize();
        //var bs = Encoding.UTF8.GetBytes(m_Host);
        //m_TempList.AddRange(BitConverter.GetBytes(bs.Length));
        //m_TempList.AddRange(bs);
        m_TempList.AddRange(BitConverter.GetBytes(m_UserID));
        return m_TempList.ToArray();
    }

    public override int Deserialize(byte[] bs, int offset = 0, int count = 0)
    {
        offset = base.Deserialize(bs, offset, count);
        //if (bs.Length - offset < 4) { return offset; }
        //var bsCount = BitConverter.ToInt32(bs, offset);
        //offset += 4;
        //if (bs.Length - offset < bsCount) { return offset; }
        //m_Host = BitConverter.ToString(bs, offset, bsCount);
        //offset += bsCount;
        if (bs.Length - offset < 4) { return offset; }
        m_UserID = BitConverter.ToUInt32(bs, offset);
        return offset + 4;
    }
}

class C2S_JoinMessage : BaseMessage
{
    public uint m_UserID;
    public override byte[] Serialize()
    {
        base.Serialize();
        m_TempList.AddRange(BitConverter.GetBytes(m_UserID));
        return m_TempList.ToArray();
    }

    public override int Deserialize(byte[] bs, int offset = 0, int count = 0)
    {
        offset = base.Deserialize(bs, offset, count);
        if (bs.Length - offset < 4) { return offset; }
        m_UserID = BitConverter.ToUInt32(bs, offset);
        offset += 4;
        return offset;
    }
}

class S2C_JoinMessage : BaseMessage
{
    public uint m_UserID;
    public Vector3 m_Pos;

    public override byte[] Serialize()
    {
        base.Serialize();
        m_TempList.AddRange(BitConverter.GetBytes(m_UserID));
        m_TempList.AddRange(BitConverter.GetBytes(m_Pos.x));
        m_TempList.AddRange(BitConverter.GetBytes(m_Pos.y));
        m_TempList.AddRange(BitConverter.GetBytes(m_Pos.z));
        return m_TempList.ToArray();
    }

    public override int Deserialize(byte[] bs, int offset = 0, int count = 0)
    {
        offset = base.Deserialize(bs, offset, count);
        if (bs.Length - offset < 4) { return offset; }
        m_UserID = BitConverter.ToUInt32(bs, offset);
        offset += 4;
        if (bs.Length - offset < 4) { return offset; }
        m_Pos.x = BitConverter.ToSingle(bs, offset);
        offset += 4;
        if (bs.Length - offset < 4) { return offset; }
        m_Pos.y = BitConverter.ToSingle(bs, offset);
        offset += 4;
        if (bs.Length - offset < 4) { return offset; }
        m_Pos.z = BitConverter.ToSingle(bs, offset);
        offset += 4;
        return offset;
    }
}

class C2S_SyncMessage : BaseMessage
{
    public uint m_UserID;
    public Vector3 m_Pos;
    public override byte[] Serialize()
    {
        base.Serialize();
        m_TempList.AddRange(BitConverter.GetBytes(m_UserID));
        m_TempList.AddRange(BitConverter.GetBytes(m_Pos.x));
        m_TempList.AddRange(BitConverter.GetBytes(m_Pos.y));
        m_TempList.AddRange(BitConverter.GetBytes(m_Pos.z));
        return m_TempList.ToArray();
    }

    public override int Deserialize(byte[] bs, int offset = 0, int count = 0)
    {
        offset = base.Deserialize(bs, offset, count);
        if (bs.Length - offset < 4) { return offset; }
        m_UserID = BitConverter.ToUInt32(bs, offset);
        offset += 4;
        if (bs.Length - offset < 4) { return offset; }
        m_Pos.x = BitConverter.ToSingle(bs, offset);
        offset += 4;
        if (bs.Length - offset < 4) { return offset; }
        m_Pos.y = BitConverter.ToSingle(bs, offset);
        offset += 4;
        if (bs.Length - offset < 4) { return offset; }
        m_Pos.z = BitConverter.ToSingle(bs, offset);
        offset += 4;
        return offset;
    }
}

class S2C_SyncMessage : BaseMessage
{
    public readonly List<S2C_SyncPlayerMessage> m_Players = new List<S2C_SyncPlayerMessage>(); 

    public override byte[] Serialize()
    {
        base.Serialize();
        m_TempList.AddRange(BitConverter.GetBytes(m_Players.Count));
        byte[] bs; 
        foreach (var item in m_Players)
        {
            bs = item.Serialize(); 
            m_TempList.AddRange(BitConverter.GetBytes(bs.Length));
            m_TempList.AddRange(bs);
        }
        return m_TempList.ToArray();
    }

    public override int Deserialize(byte[] bs, int offset = 0, int count = 0)
    {
        offset = base.Deserialize(bs, offset, count);
        if (bs.Length - offset < 4) { return offset; }
        var psCount = BitConverter.ToInt32(bs, offset);
        offset += 4;
        int pCount = 0; 
        for (int i = 0; i < psCount; i++)
        {
            if (bs.Length - offset < 4) { return offset; }
            pCount = BitConverter.ToInt32(bs, offset);
            offset += 4;
            var item = new S2C_SyncPlayerMessage();
            offset = item.Deserialize(bs, offset, pCount);
            m_Players.Add(item);
        }
        return offset;
    }
}

class S2C_SyncPlayerMessage : BaseMessage
{
    public uint m_UserID;
    public Vector3 m_Pos;

    public override byte[] Serialize()
    {
        base.Serialize();
        m_TempList.AddRange(BitConverter.GetBytes(m_UserID));
        m_TempList.AddRange(BitConverter.GetBytes(m_Pos.x));
        m_TempList.AddRange(BitConverter.GetBytes(m_Pos.y));
        m_TempList.AddRange(BitConverter.GetBytes(m_Pos.z));
        return m_TempList.ToArray();
    }

    public override int Deserialize(byte[] bs, int offset = 0, int count = 0)
    {
        int startOffset = offset;
        offset = base.Deserialize(bs, offset, count);
        if (bs.Length - offset < 4 || offset - startOffset >= count) { return offset; }
        m_UserID = BitConverter.ToUInt32(bs, offset);
        offset += 4;
        if (bs.Length - offset < 4 || offset - startOffset >= count) { return offset; }
        m_Pos.x = BitConverter.ToSingle(bs, offset);
        offset += 4;
        if (bs.Length - offset < 4 || offset - startOffset >= count) { return offset; }
        m_Pos.y = BitConverter.ToSingle(bs, offset);
        offset += 4;
        if (bs.Length - offset < 4 || offset - startOffset >= count) { return offset; }
        m_Pos.z = BitConverter.ToSingle(bs, offset);
        offset += 4;
        return offset;
    }
}

public static class MessageManager
{
    static DateTime m_StartTime;

    public static void Init()
    {
        m_StartTime = DateTime.Now;
    }

    public static bool IsRemoteIPIdentity(IPEndPoint left, IPEndPoint right)
    {
        return left.Address.Equals(right.Address) && left.Port == right.Port;
    }

    public static float GetCurElapsedTime()
    {
        return (float)(System.DateTime.Now - m_StartTime).TotalSeconds;
    }

    static Dictionary<EMessageType, System.Type> m_MsgDict = new Dictionary<EMessageType, System.Type>()
        {
            { EMessageType.HELLO, typeof(HelloMessage)},
#if SERVER
            { EMessageType.JOIN, typeof(C2S_JoinMessage)},
            { EMessageType.SYNC, typeof(C2S_SyncMessage)},
#else
            { EMessageType.JOIN, typeof(S2C_JoinMessage)},
            { EMessageType.SYNC, typeof(S2C_SyncMessage)},
#endif
        };

    static MemoryStream m_TempMemory = new MemoryStream(new byte[1024]);
    static List<byte> m_TempList = new List<byte>();

    static public byte[] Pack<T>(EMessageType type, T msg)
        where T : BaseMessage
    {
        m_TempList.Clear();
        m_TempList.AddRange(BitConverter.GetBytes((ushort)type));
        m_TempList.AddRange(msg.Serialize());
        return m_TempList.ToArray();
    }

    static public BaseMessage Unpack(byte[] bs)
    {
        var msgType = GetMessageType(bs);
        System.Type type;
        if (m_MsgDict.ContainsKey(msgType))
        {
            type = m_MsgDict[msgType];
        }
        else
        {
            type = typeof(BaseMessage);
        }
        var a = Activator.CreateInstance(type) as BaseMessage;
        a.Deserialize(bs, 2);
        return a;
    }

    static public EMessageType GetMessageType(byte[] bs)
    {
        return (EMessageType)BitConverter.ToUInt16(bs, 0);
    }

    static public EMessageType GetMessageType(BaseMessage msg)
    {
        if (msg == null)
        {
            return EMessageType.NONE;
        }

        foreach (var item in m_MsgDict)
        {
            if (item.Value.GetType() == msg.GetType())
            {
                return item.Key;
            }
        }
        return EMessageType.NONE;
    }
}
